#include "LevelLoader.h"

#include "AssertUtils.h"
#include "FileUtils.h"

// characters
#include "../Characters/Ox.h"
#include "../Characters/Dog.h"
#include "../Characters/Rat.h"
#include "../Characters/Rooster.h"

// objects
#include "../Objects/Wall.h"
#include "../Objects/Fence.h"
#include "../Objects/HayStack.h"
#include "../Objects/Rope.h"

CLevelLoader::CLevelLoader()
{
}

CLevelLoader::~CLevelLoader()
{
}

CLevelLoader* CLevelLoader::Instance()
{
	static CLevelLoader instance;
	return &instance;
}

bool CLevelLoader::LoadLevel(const std::string& levelPath, std::vector<CObject*>& objects /* out */)
{
	Json::Value root = FileUtils::ParseJsonFromFile(levelPath);
	if (root.empty())
	{
		return false;
	}

	const std::string kAngleStr = "angle";
	const std::string kPositionStr = "position";
	const std::string kObjectTypeStr = "object_type";
	const std::string kObjectLayerStr = "object_layer";
	const std::string kCharacterTypeStr = "character_type";
	const std::string kCollisionRectStr = "collision_rect";
	const std::string kUseCustomerCollisionRectStr = "use_sprite_for_collision_rect";

	for (auto it = root.begin(); it != root.end(); ++it)
	{
		CObject* newOb = nullptr;

		DebugAssert((*it).isMember(kObjectTypeStr), "No object type set!");
		ObjectType::Enum obType = ObjectType::EnumForString[(*it)[kObjectTypeStr].asString()];

		switch (obType)
		{
		case ObjectType::Character:
		{
			DebugAssert((*it).isMember(kCharacterTypeStr), "No object type set!");
			CharacterType::Enum charType = CharacterType::EnumForString[(*it)[kCharacterTypeStr].asString()];

			switch (charType)
			{
			case CharacterType::Ox:
				newOb = new COx();
				break;
			case CharacterType::Rat:
				newOb = new CRat();
				break;
			case CharacterType::Dog:
				newOb = new CDoge();
				break;
			case CharacterType::Rooster:
				newOb = new CRooster();
				break;
			default:
				DebugAssert(false, "Unhandled type %s", (*it)[kCharacterTypeStr].asCString());
				return false;
			}
			break;
		}
		//case ObjectType::HayStack:
		//	newOb = new CHayStack();
		//	break;
		//case ObjectType::Wall:
		//	newOb = new CWall();
		//	break;
		//case ObjectType::Fence:
		//	newOb = new CFence();
		//	break;
		//case ObjectType::Rope:
		//	newOb = new CRope();
		//	break;
		default:
			DebugAssert(false, "Unhandled type %s", (*it)[kObjectTypeStr].asCString());
			return false;
		}

		DebugAssert(newOb, "New object not created!");

		DebugAssert((*it).isMember(kPositionStr), "No position data set for object %s", (*it)[kObjectTypeStr].asCString());

		v2f position;
		position.x = (float)(*it)[kPositionStr]["x"].asDouble();
		position.y = (float)(*it)[kPositionStr]["y"].asDouble();

		newOb->SetPosition(position);

		DebugAssert((*it).isMember(kObjectLayerStr), "No object layer set for object %s", (*it)[kObjectLayerStr].asCString());
		newOb->SetObjectLayer(ObjectLayer::EnumForString[(*it)[kObjectLayerStr].asString()]);

		DebugAssert((*it).isMember(kAngleStr), "No angle data set for object %s", (*it)[kObjectTypeStr].asCString());
		newOb->SetAngle((float)(*it)[kAngleStr].asDouble());

		if ((*it).isMember(kUseCustomerCollisionRectStr) && (*it)[kUseCustomerCollisionRectStr].asBool())
		{
			DebugAssert((*it).isMember(kCollisionRectStr), "No overriding collision rect found for object %s!", (*it)[kObjectTypeStr].asCString());

			sf::FloatRect rect;
			rect.width = (float)(*it)[kCollisionRectStr]["width"].asDouble();
			rect.height = (float)(*it)[kCollisionRectStr]["height"].asDouble();
			rect.top = (float)(*it)[kCollisionRectStr]["top"].asDouble();
			rect.left = (float)(*it)[kCollisionRectStr]["left"].asDouble();
			newOb->SetCollisionRect(rect);
		}
		else
		{
			sf::FloatRect rect;
			rect.width = newOb->GetSprite().getLocalBounds().width;
			rect.height = newOb->GetSprite().getLocalBounds().height;
			rect.top = newOb->GetSprite().getLocalBounds().top;
			rect.left = newOb->GetSprite().getLocalBounds().left;
			newOb->SetCollisionRect(rect);
		}

		objects.push_back(newOb);
	}

	return true;
}
